package com.aliyun.messagemgt.application.service.Impl;


import com.aliyun.messagemgt.application.service.FlowControl;
import com.aliyun.messagemgt.common.constant.PushConstant;
import com.aliyun.messagemgt.dto.MsgSource;
import com.aliyun.messagemgt.util.redis.Redis;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;

import java.time.LocalDateTime;

/**
 * Created with IDEA
 *
 * @author:zhangxingxing 0027012120
 * @date:2019/4/2
 * @time:15:32
 */
@Component
public class FlowControlImpl implements FlowControl {

    private static final Logger logger = LoggerFactory.getLogger(FlowControlImpl.class);

    private static final String FLOW_CONTROL = "msgCenter.flowControl";

    private static final String LUA_SCRIPT = "local sendNum\n" +
            "local remain\n" +
            "local size = tonumber(ARGV[2])\n" +
            "local limit = tonumber(ARGV[1])\n" +
            "sendNum = redis.call('GET',KEYS[1])\n" +
            "if (not sendNum )\n" +
            "then\n" +
            "\tsendNum = 0\n" +
            "else\n" +
            "\tsendNum = tonumber(sendNum) \n" +
            "end\n" +
            "remain = limit - sendNum\n" +
            "if (remain >= size)\n" +
            "then\n" +
            "\tredis.call('SET',KEYS[1],size + sendNum,'EX',KEYS[2])\n" +
            "\treturn -1\n" +
            "else\n" +
            "\tredis.call('SET',KEYS[1],limit,'EX',KEYS[2])\n" +
            "\treturn remain\n" +
            "end";
    /**
     * 分钟过期秒数
     */
    private static final Integer MIN_EXP = 60;
    /**
     * 小时过期秒数
     */
    private static final Integer HOUR_EXP = 60 * 60;
    /**
     * 天过期秒数
     */
    private static final Integer DAY_EXP = 24 * 60 * 60;

    /**
     * 1.msgSource中带有流控规则
     * 2.取流控单位和值，根据相应的规则生成key FLOW_CONTROL.MSGID.TIME
     * 3.到redis中查询
     * => 不存在，即已发送值为0，存在即当前值为redis value值
     * 4.可发送值 a = limit - 已发送；if size <= a return -1;则无限制, else return a
     * 5.更新redis key值
     *
     * @param size
     * @param msgSource
     * @return -1 表示无限制或在限制内，其他表示允许发送量
     */
    @Override
    public int inControl(int size, MsgSource msgSource) {
        // msgSource为空时，默认不做流控
        if (msgSource.getFlowControlCountLimit() == null) {
            return -1;
        }

        String resourceId = msgSource.getSourceCode();
        Integer timeUnit = msgSource.getFlowControlTimeUnit();
        Integer limit = msgSource.getFlowControlCountLimit();

        Integer expireTime;
        Integer nowByTimeUnit;
        switch (timeUnit) {
            case PushConstant.MIN_FLOW_CONTROL:
                expireTime = MIN_EXP;
                nowByTimeUnit = LocalDateTime.now().getMinute();
                break;
            case PushConstant.HOUR_FLOW_CONTROL:
                expireTime = HOUR_EXP;
                nowByTimeUnit = LocalDateTime.now().getHour();
                break;
            case PushConstant.DAY_FLOW_CONTROL:
                expireTime = DAY_EXP;
                nowByTimeUnit = LocalDateTime.now().getDayOfMonth();
                break;
            default:
                expireTime = MIN_EXP;
                nowByTimeUnit = LocalDateTime.now().getMinute();
        }

        StringBuilder redisKey = new StringBuilder().append(FLOW_CONTROL).append(".")
                .append(resourceId).append(".")
                .append(timeUnit).append(".")
                .append(nowByTimeUnit);

        String key = redisKey.toString();
        logger.info("key {}", key);
        Jedis jedis = Redis.getJedis();
        Object sendSize = jedis.eval(LUA_SCRIPT, 2, key, String.valueOf(expireTime), String.valueOf(limit), String.valueOf(size));
        jedis.close();
        int sendNum = 0;
        if (sendSize != null) {
            sendNum = Integer.valueOf(sendSize.toString());
        }
        return sendNum;
    }

}
